home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Technotools
/
Technotools (Chestnut CD-ROM)(1993).ISO
/
lang_c
/
mnsbm
/
dir.cb
next >
Wrap
Text File
|
1989-10-31
|
25KB
|
909 lines
/* */
/* dir.m: */
/* A full-fledged, integrated environment for manipulating files */
/* and directories from within BRIEF. Uses the dialog manager. */
/* If sorting is desired, also pulls in sort macros. */
/* */
/* Written by Dan Teven. Preliminary version, August 6, 1987. */
/* Requires the dialog manager from BRIEF version 2.01. */
/* */
#include "dialog.h"
/* These definitions are the names of temporary files that the directory */
/* macro will use. You can change them to specify a full path name. */
/* For best performance, specify a temp file on a RAMdisk. */
#define TEMP_FILE "dir.tmp"
#define ERROR_FILE "dir.err"
#define TAG_CHAR "√"
/* These numbers define the column positions for the various fields */
/* of the directory listing (with the format defined here and no tabs). */
/* The full definition is a sort command for the buffer. */
#define NAME_SORT "_dir_sort"
#define EXT_SORT "_dir_sort 11"
#define SIZE_SORT "_dir_sort 15"
#define TIME_SORT "_dir_sort 25"
/* The tag column is with tabs 6. */
#define TAG_COL 47
/* */
/* _init: */
/* Initialize global variables. The only two global strings are */
/* used to hold the current directory path and file spec. */
/* */
_init ()
{
int _dir_buffer,
_dir_tags;
string _dir_wd,
_filespec;
global _dir_wd,
_filespec,
_dir_buffer,
_dir_tags;
}
/* */
/* dir: */
/* The directory macro. The syntax is dir [pathspec] where */
/* pathspec is an optional alternate path or file specifier to */
/* use. */
/* */
dir (...)
{
extern add_to_path;
int last_loc,
current_buffer;
string initial_wd;
/* Get the current directory so we can change back to it upon */
/* exiting. Make that the default. */
getwd ("", initial_wd);
_dir_wd = initial_wd;
/* If no directory spec passed on the command line, we use */
/* the default. We use the passed spec if possible. */
if (!get_parm (0, _filespec))
_filespec = _dir_wd;
/* Try to change immediately to the filespec entered as if */
/* it were a directory. If we can change to it, it becomes */
/* the default for future invocations. */
if (cd (_filespec))
{
getwd ("", _dir_wd);
_filespec = "*.*";
}
else
/* OK, so it wasn't just a directory. */
/* If we have a backslash in the path specifier, split it */
/* into a new directory specifier and file specifier. */
/* If not, it must be all file specifier, which implies */
/* use of the current directory. */
{
if (last_loc = search_string ("\\\\[~\\\\/]+", _filespec + "", TRUE))
{
_dir_wd = substr (_filespec, 1, last_loc - 1);
_filespec = substr (_filespec, last_loc + 1);
}
/* Try to change to the directory specified. If the cd */
/* fails, we know the directory is bad, and we reset it to */
/* the current directory and exit. */
if (cd (_dir_wd))
getwd ("", _dir_wd);
else
{
error ("Invalid path specified.");
beep ();
return FALSE;
}
}
message ("Creating directory list...");
/* Create a temporary file containing the directory listing */
/* for _filespec, and read it in. We are already in the */
/* directory referred to. */
current_buffer = inq_buffer ();
set_buffer (_dir_buffer = create_buffer ("Directory", NULL, TRUE));
dos ("dir " + (_filespec + (" >&" + TEMP_FILE)));
read_file (TEMP_FILE);
/* Determine if any files matched the file spec. If none did, */
/* we clean up and exit. */
if (search_back ("<File not found", TRUE, TRUE))
{
error ("No files found.");
beep ();
}
else
{
_reformat_directory ();
_process_menu (4, 20, 54, 2, "", "Press Alt-H for Help", NULL, _dir_buffer, "_dir_action", TRUE);
}
/* Delete the directory buffer, and the file(s) associated with */
/* it. Reset the packages for the current buffer. */
set_buffer (current_buffer);
call_registered_macro (1);
delete_buffer (_dir_buffer);
del (TEMP_FILE);
/* Change back to the original directory, and return. */
cd (initial_wd);
return TRUE;
}
/* */
/* _reformat_directory: */
/* Takes a buffer (named by the global variable _dir_buffer) and */
/* converts it to a more readable and useful menu-style format. */
/* */
_reformat_directory ()
{
string name;
/* Delete the first four lines of the file, which are two */
/* blank lines, the volume label, and the directory specifier. */
move_abs (4, 1);
drop_anchor (3);
top_of_buffer ();
delete_block ();
/* Insert a tab before every remaining line of the file. Then */
/* insert two delimiters into the file, and sort it so that */
/* all the directories are in one list and all the files are */
/* in the other. */
translate ("<{[~]*}>", "\\t\\0 ;_dir_tag_line", TRUE, TRUE);
insert ("Directories:\nFiles:\n");
while (search_fwd ("<DIR>", FALSE, TRUE))
{
drop_anchor (3);
cut ();
search_back ("<F", TRUE, TRUE);
paste ();
}
/* Remove the <DIR> marker for the directories, which we */
/* know are all before the current position at this point. */
/* Also, change the action to _dir_chdir. */
translate ("<DIR> ", " ", TRUE, FALSE, TRUE, FALSE, FALSE);
translate (";_dir_tag_line", ";_dir_chdir", TRUE, FALSE, TRUE, FALSE, FALSE);
/* Delete the last lines of the buffer, which are the */
/* number of files/free space and a blank line. */
end_of_buffer ();
up ();
delete_line ();
delete_line ();
tabs (6);
top_of_buffer ();
_dir_tags = 0;
}
/* */
/* _dir_action: */
/* Handles events passed to it by the dialog manager. */
/* */
_dir_action (...)
{
int event_type;
string button_text;
get_parm (0, event_type);
switch (event_type)
{
case DIALOG_INIT:
{
assign_to_key ("<Alt-A>", "_dir_tag_all");
assign_to_key ("<Alt-C>", "_dir_copy");
assign_to_key ("<Alt-D>", "_dir_delete");
assign_to_key ("<Alt-E>", "_for_tagged _dir_edit");
assign_to_key ("<Alt-H>", "_dir_help");
assign_to_key ("<Alt-N>", NAME_SORT);
assign_to_key ("<Alt-R>", "_for_tagged _dir_rename");
assign_to_key ("<Alt-S>", SIZE_SORT);
assign_to_key ("<Alt-T>", TIME_SORT);
assign_to_key ("<Alt-X>", EXT_SORT);
assign_to_key ("<Alt-Z>", "dos");
button_text = "Directory of " + _dir_wd;
/* If the directory name is the root, we don't */
/* add the \ before the file specifier. */
if (strlen (_dir_wd) > 3)
button_text += "\\";
message (button_text + (_filespec + "."));
}
case DIALOG_TERM:
{
assign_to_key ("<Alt-A>", "nothing");
assign_to_key ("<Alt-C>", "nothing");
assign_to_key ("<Alt-D>", "nothing");
assign_to_key ("<Alt-E>", "nothing");
assign_to_key ("<Alt-H>", "nothing");
assign_to_key ("<Alt-N>", "nothing");
assign_to_key ("<Alt-R>", "nothing");
assign_to_key ("<Alt-S>", "nothing");
assign_to_key ("<Alt-T>", "nothing");
assign_to_key ("<Alt-X>", "nothing");
assign_to_key ("<Alt-Z>", "nothing");
}
/* We check to make sure there's a semicolon on the line */
/* before we move to it. */
case DIALOG_ALTER_MENU:
{
get_parm (2, button_text);
if (!index (button_text, ";"))
return FALSE;
}
/* If a menu button is picked, we execute the action */
/* associated with the button. This action will be either */
/* changing to another directory or tagging a file. */
case DIALOG_PICK_MENU:
{
get_parm (2, button_text);
execute_macro (substr (button_text, index (button_text, ";") + 1));
}
}
returns TRUE;
}
/* _dir_tag_all: */
/* If there are tagged files, untags them all. Otherwise, tags */
/* all the files. */
_dir_tag_all ()
{
save_position ();
top_of_buffer ();
/* If any lines are tagged, untag them. If none are, tag */
/* them all. */
if (_dir_tags)
{
_dir_tags = 0;
translate (TAG_CHAR, " ", TRUE, FALSE);
}
else
{
search_fwd ("<F", TRUE, TRUE);
down ();
while (!inq_position ())
{
_dir_tag_line ();
down ();
}
}
restore_position ();
}
/* */
/* _dir_tag_line: */
/* Toggles the tag state of a particular file. */
/* */
_dir_tag_line (...)
{
move_abs (0, TAG_COL);
if (read (1) == TAG_CHAR)
{
insert (" ");
--_dir_tags;
}
else
{
insert (TAG_CHAR);
++_dir_tags;
}
delete_char ();
beginning_of_line ();
}
/* */
/* _dir_filename: */
/* Reads and returns the filename the cursor is located at. */
/* If the name is not a file or directory, returns "". */
/* */
_dir_filename (...)
{
int space_loc,
ext_loc;
string file_name;
/* If we have no semicolon on this line, it's not the name of */
/* a file or directory, so we return a null string. */
if (!index (file_name = read (), ";"))
return "";
/* Trim leading and trailing white space (and the rest of the */
/* line) from the name. If we have any white space left, */
/* it's in the middle and must be replaced by a period. */
file_name = lower (trim (ltrim (substr (file_name, 1, 13))));
if (space_loc = index (file_name, " "))
{
ext_loc = rindex (file_name, " ") + 1;
file_name = (substr (file_name, 1, --space_loc) + ".") + substr (file_name, ext_loc);
}
return file_name;
}
/* */
/* _dir_tagged: */
/* Returns TRUE if the current file is tagged, FALSE otherwise. */
/* */
int _dir_tagged ()
{
int val;
move_abs (0, TAG_COL);
val = read (1) == TAG_CHAR;
refresh();
beginning_of_line ();
return val;
}
/* */
/* _dir_chdir: */
/* Changes the current directory and redisplays. */
/* */
_dir_chdir (...)
{
string new_dir;
/* Read the directory name from the buffer, and trim leading and */
/* trailing white space from it. If we have any white space left, */
/* it's in the middle and must be replaced by a period. If we */
/* have just a period, we return; we're already on the current */
/* directory. */
if ((new_dir = _dir_filename ()) == ".")
{
beep ();
return;
}
/* Change directory. Since we may not be coming back, make */
/* sure we remove the TEMP_FILE first. */
message ("Changing directory...");
del (TEMP_FILE);
/* Change to the new directory, which we know is a legal directory, */
/* and get the new working directory name. Write a new temp file. */
/* Delete the existing buffer and read in the new one; reformat */
/* it; bring it into the window. */
cd (new_dir);
getwd ("", _dir_wd);
new_dir = _dir_wd;
_filespec = "*.*";
_dir_refresh ();
/* If the directory is the root, we don't */
/* add the \ before the file specifier. */
if (strlen (new_dir) > 3)
new_dir += "\\";
message ("Directory of %s*.*.", new_dir);
}
/* */
/* _dir_refresh: */
/* Calls DOS to write the directory to the temp file, deletes */
/* the contents of the current buffer, and reads the temp file */
/* back in. Reformats the directory and restores the highlight */
/* (that delete_block removed). */
/* */
_dir_refresh (...)
{
extern _dialog_menu_home;
dos ("dir " + (_filespec + (">&" + TEMP_FILE)));
raise_anchor ();
top_of_buffer ();
drop_anchor (1);
end_of_buffer ();
prev_char ();
delete_block ();
read_file (TEMP_FILE);
top_of_buffer ();
_reformat_directory ();
_dialog_menu_home ();
}
/* */
/* _for_tagged: */
/* For all the lines that are tagged, performs another macro */
/* that is passed to it as a parameter. If no lines are tagged, */
/* performs the macro on the current line. */
/* */
_for_tagged (...)
{
string action;
get_parm (0, action);
if (_dir_tags)
{
raise_anchor ();
save_position ();
top_of_buffer ();
search_fwd ("<F", TRUE, TRUE);
down ();
while (!inq_position ())
{
if (_dir_tagged ())
execute_macro (action);
down ();
}
restore_position ();
drop_anchor (3);
message ("Done.");
}
else
execute_macro (action);
}
/* */
/* _dir_edit: */
/* Parses a filename from the current line, and creates a buffer */
/* for it. Does not remove the directory menu. */
/* */
_dir_edit (...)
{
string file_name;
int not_legal_file,
old_buffer;
/* If the current line is not a file, we don't want to edit */
/* it. The best way to tell if it is a file is to search */
/* forward for the Files: marker; if we don't find it, it's */
/* a file. */
save_position ();
not_legal_file = search_fwd ("<F", TRUE, TRUE);
restore_position ();
if (not_legal_file)
beep ();
else
{
file_name = _dir_filename ();
old_buffer = inq_buffer ();
set_buffer (create_buffer (file_name, file_name, FALSE));
call_registered_macro (1);
set_buffer (old_buffer);
call_registered_macro (1);
message ("File %s edited.", file_name);
}
}
/* */
/* _dir_copy: */
/* Prompts for a destination to copy to. If files are tagged, */
/* copies all the tagged files to the same destination. If not, */
/* copies the current file to the destination. Redraws the menu */
/* when it's done. */
/* */
_dir_copy (...)
{
string prompt;
/* Prompt for the destination to copy the file to. If Esc */
/* is pressed, do not copy it. */
if (_dir_tags)
sprintf (prompt, "Copy %d tagged files to: ", _dir_tags);
else
{
if ((prompt = _dir_filename ()) == "")
{
beep ();
return;
}
prompt = "Copy " + (prompt + " to: ");
}
if (get_parm (0, prompt, prompt, 64))
{
_for_tagged ("__dir_copy " + prompt);
message ("Updating directory...");
raise_anchor ();
save_position ();
_dir_refresh ();
restore_position ();
drop_anchor (3);
message ("");
}
else
message ("Copy cancelled.");
}
/* */
/* __dir_copy: */
/* Lower level routine that copies the file where the */
/* cursor is located to the passed destination. */
/* */
__dir_copy (...)
{
string command,
destination;
int error_buf;
/* Build the copy command. We are already in the same */
/* directory as the source file, so we don't need a full */
/* path name for that file. We allow a path name for the */
/* destination file. */
if ((destination = _dir_filename ()) == "")
{
beep ();
return;
}
message ("Copying %s...", destination);
sprintf (command, "copy %s ", destination);
get_parm (0, destination);
command += destination + (" >&" + ERROR_FILE);
/* Copy the file, and get the error file (if any) into */
/* a system buffer. This is the only way to check for errors. */
dos (command);
set_buffer (error_buf = create_buffer (ERROR_FILE, ERROR_FILE, TRUE));
/* Check for errors in the file. */
if (search_fwd ("File(s) copied", FALSE, TRUE))
message ("Copied.");
else
{
error ("%s.", trim (ltrim (read ())));
beep ();
}
/* Delete the error buffer and file, and change back to */
/* the directory buffer. */
set_buffer (_dir_buffer);
delete_buffer (error_buf);
del (ERROR_FILE);
}
/* */
/* _dir_delete: */
/* If files are tagged, deletes them all after confirmation. */
/* If none are, deletes the marked file after confirmation. */
/* (If a directory is selected, beeps.) */
/* */
_dir_delete (...)
{
string response;
int not_legal_file;
/* Only ask for confirmation once. It will be handy to be */
/* able to delete groups of files without being nagged. */
/* If files are marked, we only use those; if none are, we */
/* have to make sure that a file is selected. */
if (_dir_tags)
sprintf (response, "Delete %d tagged files from disk [yn]? ", _dir_tags);
else
{
save_position ();
not_legal_file = search_fwd ("<F", TRUE, TRUE);
restore_position ();
if (not_legal_file)
{
beep ();
return;
}
sprintf (response, "Delete %s from disk [yn]? ", _dir_filename ());
}
/* If we can proceed, we delete all the files. Note that */
/* __dir_delete has to move up after deleting a line from */
/* the buffer so that all tagged files will be seen. This */
/* is why we move down afterward, then move back up while we */
/* are past EOF. We can never end up past EOF, and we end */
/* up where we were before if there are files below which */
/* can move up in the list. */
if (get_parm (0, response, response, 1) && lower (response) == "y")
{
_for_tagged ("__dir_delete");
raise_anchor ();
down ();
while (inq_position ())
up ();
drop_anchor (3);
}
else
message ("Deletion cancelled.");
}
/* */
/* __dir_delete: */
/* Lower level call to read a filename and actually delete it. */
/* Corrects the display by removing the line the file was on. */
/* */
__dir_delete (...)
{
string file_name,
temp;
int file_attrib;
file_name = _dir_filename ();
temp = add_to_path (_dir_wd, file_name);
if (del (file_name) <= 0)
/* We know the file exists, since it's in our list. */
/* (If the user did something stupid during an Alt-Z, */
/* that's his problem.) */
/* We know that system, hidden, and volume files do */
/* not make it into the directory list, and directories */
/* are already sorted out; so the only weird attribute */
/* the file could have is read-only. If the file is */
/* read-only, that's why the del failed. If not, it */
/* must have failed because the file's being edited. */
{
file_pattern (file_name);
find_file (NULL, NULL, NULL, NULL, file_attrib);
if (file_attrib & 0x0001)
{
error ("File %s is read-only.", file_name);
beep ();
}
else
{
error ("You can't delete an edited file.");
beep ();
}
return;
}
if (_dir_tagged ())
{
error ("decrementing _dir_tags %d", _dir_tags);
--_dir_tags;
}
delete_line ();
up ();
message ("File %s deleted.", file_name);
}
/* */
/* _dir_rename: */
/* For the current file, prompts for a new name, and attempts */
/* to rename it. Uses command-level rename, and so does not work */
/* across directories. */
/* */
_dir_rename (...)
{
string prompt,
destination,
file_name;
int error_buf,
ext_loc;
/* If we are not on a file name, beep. */
save_position ();
ext_loc = search_fwd ("<F", TRUE, TRUE);
restore_position ();
if (ext_loc)
{
beep ();
return;
}
/* First, get the destination. We allow only a 12-character */
/* response since path names are not legal. Then build the */
/* rename command. */
sprintf (prompt, "Rename %s to: ", file_name = _dir_filename ());
if (get_parm (0, destination, prompt, 12))
{
sprintf (prompt, "rename %s ", file_name);
prompt += destination + (" >&" + ERROR_FILE);
/* Rename the file, and get the error file (if any) into */
/* a system buffer. This is the only way to catch errors. */
dos (prompt);
set_buffer (error_buf = create_buffer (ERROR_FILE, ERROR_FILE, TRUE));
/* Check for errors in the file. If there were none, */
/* update the screen with the new name. */
if (read (1) == "\n")
{
message ("Renamed.");
set_buffer (_dir_buffer);
drop_anchor (1);
move_abs (0, 17);
delete_block ();
if (ext_loc = index (destination, "."))
{
sprintf (file_name, "\t%-8s ", substr (destination, 1, ext_loc - 1));
sprintf (prompt, "%-3s", substr (destination, ext_loc + 1));
file_name += prompt;
}
else
sprintf (file_name, "\t%-12s", destination);
insert (upper (file_name));
beginning_of_line ();
}
else
{
error ("%s.", trim (ltrim (read ())));
beep ();
}
/* Delete the error buffer and file, and change back to */
/* the directory buffer. */
set_buffer (_dir_buffer);
delete_buffer (error_buf);
del (ERROR_FILE);
}
else
message ("Rename cancelled.");
}
/* */
/* _dir_sort: */
/* Sorts the files and directories (separately) on one of several */
/* criteria, including name, extension, size, and time. */
/* */
_dir_sort (...)
{
int sort_col;
string sort_command;
extern sort_block;
/* Do a little error checking. */
if (!inq_macro ("sort_buffer") && load_macro ("sort") <= 0)
{
error ("Unable to load sort macro.");
beep ();
return;
}
if (get_parm (0, sort_col))
sprintf (sort_command, "sort_block %d", sort_col);
else
sort_command = "sort_block";
/* Turn off the status messages while we sort. Temporarily */
/* raise the line mark and save its location, while we use */
/* another line mark to delimit the areas to sort. */
raise_anchor ();
save_position ();
/* Mark the directories in the list to be sorted first, then sort. */
/* Note that sort_block and _sort_on_field remove the anchor. */
top_of_buffer ();
down ();
drop_anchor (3);
search_fwd ("<F", TRUE, TRUE);
up ();
execute_macro (sort_command);
/* Mark the files, then sort. */
search_fwd ("<F", TRUE, TRUE);
down ();
drop_anchor (3);
end_of_buffer ();
execute_macro (sort_command);
/* Clean up. Put the cursor where we found it, and restore */
/* the mark. Clear the message line. */
restore_position ();
drop_anchor (3);
}
/* */
/* _dir_help: */
/* Finds the help file in BHELP and displays it in a pop-up */
/* window. Clears the window when any key is pressed. */
/* */
_dir_help (...)
{
int help_buffer;
set_buffer (help_buffer = create_buffer ("Help", "c:/brief/help/dir.txt", TRUE));
create_window (16, 21, 49, 5, "Any key to resume");
attach_buffer (help_buffer);
refresh ();
while (!inq_kbd_char ())
;
read_char ();
set_buffer (_dir_buffer);
delete_window ();
delete_buffer (help_buffer);
}